Credit Card Dataset for Clustering

Author

Mantzari Rosemarie

Δεδομένα dataset

Φόρτωση Δεδομένων

Το πρώτο βήμα είναι η συλλογή και η προετοιμασία των δεδομένων για ανάλυση. Στο unsupervised learning, τα δεδομένα δεν έχουν προκαθορισμένες ετικέτες, επομένως o καθαρισμός, η μετατροπή και η κανονικοποίηση των δεδομένων για την αφαίρεση τυχόν θορύβου ή ακραίων στοιχείων που μπορεί να επηρεάσουν την ανάλυση είναι αναγκαία.

# %pip install seaborn
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
warnings.filterwarnings("ignore")
from sklearn.preprocessing import StandardScaler, Normalizer, normalize
from sklearn.cluster import KMeans
from sklearn.decomposition import PCA
from sklearn import metrics
from sklearn.metrics import silhouette_score
from sklearn.cluster import AgglomerativeClustering
from scipy.cluster.hierarchy import dendrogram, linkage
from sklearn.metrics.pairwise import cosine_similarity
# Συνάρτηση για plot
def plot_num_cat(feature, target, figsize=None):
    fig = plt.figure(figsize=(8,4))

    for value in df[target].unique():
        sns.kdeplot(df[df[target]==value][feature])

    fig.legend(labels=df[target].unique())
    plt.title('{} distribution based on {}'.format(feature, target))
    plt.show()
# Φόρτωση των δεδομένων
df = pd.read_csv('C:\\Users\\Petros\\Desktop\\university\\4ο έτος\\8 semester\\εξόρυξη μεγάλου όγκου δεδομένων\\mining\\CC GENERAL.csv')
df.head()
CUST_ID BALANCE BALANCE_FREQUENCY PURCHASES ONEOFF_PURCHASES INSTALLMENTS_PURCHASES CASH_ADVANCE PURCHASES_FREQUENCY ONEOFF_PURCHASES_FREQUENCY PURCHASES_INSTALLMENTS_FREQUENCY CASH_ADVANCE_FREQUENCY CASH_ADVANCE_TRX PURCHASES_TRX CREDIT_LIMIT PAYMENTS MINIMUM_PAYMENTS PRC_FULL_PAYMENT TENURE
0 C10001 40.900749 0.818182 95.40 0.00 95.4 0.000000 0.166667 0.000000 0.083333 0.000000 0 2 1000.0 201.802084 139.509787 0.000000 12
1 C10002 3202.467416 0.909091 0.00 0.00 0.0 6442.945483 0.000000 0.000000 0.000000 0.250000 4 0 7000.0 4103.032597 1072.340217 0.222222 12
2 C10003 2495.148862 1.000000 773.17 773.17 0.0 0.000000 1.000000 1.000000 0.000000 0.000000 0 12 7500.0 622.066742 627.284787 0.000000 12
3 C10004 1666.670542 0.636364 1499.00 1499.00 0.0 205.788017 0.083333 0.083333 0.000000 0.083333 1 1 7500.0 0.000000 NaN 0.000000 12
4 C10005 817.714335 1.000000 16.00 16.00 0.0 0.000000 0.083333 0.083333 0.000000 0.000000 0 1 1200.0 678.334763 244.791237 0.000000 12

Ακολουθεί το λεξικό δεδομένων για το σύνολο δεδομένων πιστωτικών καρτών:

  • CUST_ID : Ταυτότητα κατόχου πιστωτικής κάρτας
  • BALANCE : Ποσό υπολοίπου που απομένει στον λογαριασμό τους για να κάνουν αγορές
  • BALANCE_FREQUENCY : Πόσο συχνά ενημερώνεται το Υπόλοιπο (1 = ενημερώνεται συχνά, 0 = δεν ενημερώνεται συχνά)
  • PURCHASES : Ποσό αγορών που έγιναν από λογαριασμό
  • ONEOFF_PURCHASES : Το μέγιστο ποσό αγοράς πραγματοποιείται με μία κίνηση
  • INSTALLMENTS_PURCHASES : Ποσό αγοράς που έγινε με δόσεις
  • CASH_ADVANCE: Μετρητά προκαταβολικά από τον χρήστη
  • PURCHASES_FREQUENCY: Πόσο συχνά γίνονται οι αγορές (1 = αγοράζεται συχνά, 0 = δεν αγοράζεται συχνά)
  • ONEOFFPURCHASESFREQUENCY: Πόσο συχνά γίνονται οι αγορές με μία κατάθεση (1 = αγοράζεται συχνά, 0 = δεν αγοράζεται συχνά)
  • PURCHASESINSTALLMENTSFREQUENCY : Πόσο συχνά γίνονται οι αγορές σε δόσεις (1 = γίνονται συχνά, 0 = δεν γίνονται συχνά)
  • PURCHASESINSTALLMENTSFREQUENCY : Πόσο συχνά καταβάλλονται τα μετρητά προκαταβολικά
  • CASHADVANCETRX : Αριθμός συναλλαγών που πραγματοποιήθηκαν με “Cash in Advanced”
  • PURCHASES_TRX : Αριθμός συναλλαγών αγοράς που πραγματοποιήθηκαν
  • CREDIT_LIMIT : Όριο πιστωτικής κάρτας για τον χρήστη
  • PAYMENTS: Ποσό πληρωμής που έγινε από τον χρήστη
  • MINIMUM_PAYMENTS : Ελάχιστο ποσό πληρωμών που πραγματοποιούνται από τον χρήστη
  • PRCFULLPAYMENT: Ποσοστό της πλήρους πληρωμής που καταβάλλεται από τον χρήστη
  • TENURE : Περίοδος θητείας της υπηρεσίας πιστωτικής κάρτας για τον χρήστη

Έρευνα Δεδομένων

Σε αυτό το σημείο πρέπει να ελέγξουμε την ποιότητα των δεδομένων και να αξιολογήσουμε τυχόν προβλήματα στα δεδομένα όπως:

  • αν υπάρχουν μηδενικές τιμές σε κάθε στήλη
  • αν κάθε στήλη έχει τον κατάλληλο τύπο δεδομένων
  • αν υπάρχουν ακραίες τιμές
  • αν υπάρχουν διπλότυπες σειρές
  • κατανομή για κάθε στήλη (skewness)
# Τύποι δεδομένων και non-null τιμές κάθε στήλης
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 8950 entries, 0 to 8949
Data columns (total 18 columns):
 #   Column                            Non-Null Count  Dtype  
---  ------                            --------------  -----  
 0   CUST_ID                           8950 non-null   object 
 1   BALANCE                           8950 non-null   float64
 2   BALANCE_FREQUENCY                 8950 non-null   float64
 3   PURCHASES                         8950 non-null   float64
 4   ONEOFF_PURCHASES                  8950 non-null   float64
 5   INSTALLMENTS_PURCHASES            8950 non-null   float64
 6   CASH_ADVANCE                      8950 non-null   float64
 7   PURCHASES_FREQUENCY               8950 non-null   float64
 8   ONEOFF_PURCHASES_FREQUENCY        8950 non-null   float64
 9   PURCHASES_INSTALLMENTS_FREQUENCY  8950 non-null   float64
 10  CASH_ADVANCE_FREQUENCY            8950 non-null   float64
 11  CASH_ADVANCE_TRX                  8950 non-null   int64  
 12  PURCHASES_TRX                     8950 non-null   int64  
 13  CREDIT_LIMIT                      8949 non-null   float64
 14  PAYMENTS                          8950 non-null   float64
 15  MINIMUM_PAYMENTS                  8637 non-null   float64
 16  PRC_FULL_PAYMENT                  8950 non-null   float64
 17  TENURE                            8950 non-null   int64  
dtypes: float64(14), int64(3), object(1)
memory usage: 1.2+ MB
# Ύπαρξη διπλότυπων στηλών 
df.duplicated().sum()
0

Η έξοδος που προκύπτει από το παρακάτω απόσπασμα κώδικα είναι ένα πλέγμα ιστογραμμάτων, όπου κάθε γράφημα αντιπροσωπεύει την κατανομή ενός αριθμητικού χαρακτηριστικού στο DataFrame. Αυτή η οπτικοποίηση μας επιτρέπει να δούμε την κατανομή και το εύρος τιμών για κάθε αριθμητικό χαρακτηριστικό στο σύνολο δεδομένων.

# Ιστογράμματα χαρακτηριστικών
numerical_features=[feature for feature in df.columns if df[feature].dtypes!='object']
df[numerical_features].hist(bins=15, figsize=(15, 15), layout=(6, 3));
print(numerical_features)
['BALANCE', 'BALANCE_FREQUENCY', 'PURCHASES', 'ONEOFF_PURCHASES', 'INSTALLMENTS_PURCHASES', 'CASH_ADVANCE', 'PURCHASES_FREQUENCY', 'ONEOFF_PURCHASES_FREQUENCY', 'PURCHASES_INSTALLMENTS_FREQUENCY', 'CASH_ADVANCE_FREQUENCY', 'CASH_ADVANCE_TRX', 'PURCHASES_TRX', 'CREDIT_LIMIT', 'PAYMENTS', 'MINIMUM_PAYMENTS', 'PRC_FULL_PAYMENT', 'TENURE']

  • Οι περισσότερες στήλες έχουν τεράστιο αριθμό τιμών 0.
  • Επιπλέον, οι περισσότερες στήλες είναι πολύ λοξές προς τα δεξιά, όπως φαίνεται από τα ιστογράμματα.
  • Κατά την περαιτέρω διερεύνηση της στήλης BALANCE, φαίνεται ότι υπάρχουν πολλές πιστωτικές κάρτες με 0 υπόλοιπα.
  • Υποστηρίζεται επίσης με τόσα 0 ποσά αγοράς στη στήλη PURCHASES.
  • Οι περισσότεροι λογαριασμοί πιστωτικών καρτών έχουν 1 βαθμολογία στη στήλη BALANCE_FREQUENCY, κάτι που δείχνει ότι οι περισσότεροι πελάτες χρησιμοποιούν συχνά την πιστωτική κάρτα.
  • Ωστόσο, αυτό διαφέρει από το ONEOFF_PURCHASES και το PURCHASES_INSTALLMENT_FREQUENCY, όπου η πλειοψηφία των πελατών δεν χρησιμοποιεί πιστωτικές κάρτες για εφάπαξ συναλλαγές ή πληρωμές σε δόσεις.

Στήλες χωρίς δεδομένα: με τον παρακάτω κώδικα μπορούμε να δούμε ποιές στήλες περιέχουν null τιμές και πόσες φορές εμφανίζονται αυτές.

# Στήλες που περιέχουν null τιμες
df.isna().sum().sort_values(ascending=False).head(5)
MINIMUM_PAYMENTS    313
CREDIT_LIMIT          1
CUST_ID               0
BALANCE               0
PRC_FULL_PAYMENT      0
dtype: int64

Συμπεραίνουμε πως λείπουν τιμές στο CREDIT_LIMIT (1 τιμή λείπει) και στο MINIMUM_PAYMENTS (313 τιμές λείπουν).

Καθαρισμός Δεδομένων

Ο καθαρισμός δεδομένων περιλαμβάνει την αφαίρεση δεδομένων που λείπουν, καθώς και τη διόρθωση τυχόν σφαλμάτων ή ασυνεπειών στα δεδομένα.

# Αντίγραφο του dataset
df_copy=df.copy()

Το CUST_ID θα αφαιρεθεί επειδή έχει μοναδικές τιμές.

df_copy = df_copy.drop(['CUST_ID'], axis=1)

Ο παρακάτω κώδικας συμπληρώνει τις τιμές που λείπουν στις στήλες “CREDIT_LIMIT” και “MINIMUM_PAYMENTS” του DataFrame df_copy με τις μέσες τιμές των αντίστοιχων στηλών τους. Χρησιμοποιεί τη συνάρτηση fillna() για να αντικαταστήσει τις τιμές που λείπουν με τη μέση τιμή που υπολογίζεται χρησιμοποιώντας τη συνάρτηση mean(). Η παράμετρος inplace=True διασφαλίζει ότι οι αλλαγές γίνονται απευθείας στο DataFrame df_copy χωρίς να δημιουργηθεί νέο αντίγραφο.

df_copy["CREDIT_LIMIT"].fillna(df_copy["CREDIT_LIMIT"].mean(), inplace=True)
df_copy["MINIMUM_PAYMENTS"].fillna( df_copy["MINIMUM_PAYMENTS"].mean(), inplace=True)

Heatmap των δεδομένων

plt.figure(figsize=(10, 10))
sns.heatmap(round(df[numerical_features].corr(method='spearman'), 2), 
            annot=True, mask=None, cmap='GnBu')
plt.show()

Μετασχηματισμός δεδομένων

Ο μετασχηματισμός δεδομένων περιλαμβάνει τη μετατροπή των δεδομένων σε μια μορφή κατάλληλη για ανάλυση. Αυτό μπορεί να περιλαμβάνει την κλιμάκωση ή την κανονικοποίηση των δεδομένων. Εμείς θα εφαρμόσουμε κλιμάκωση.

# Κλιμάκωση των δεδομένων
scaler = StandardScaler() 
scaled_df = scaler.fit_transform(df_copy) 

Ομαδοποίηση με KMeans

Οι αλγόριθμοι ομαδοποίησης ομαδοποιούν παρόμοια σημεία δεδομένων με βάση τα χαρακτηριστικά τους. Αυτό μπορεί να είναι χρήσιμο για τον εντοπισμό προτύπων ή δομής στα δεδομένα ή για την τμηματοποίηση των δεδομένων σε διαφορετικές ομάδες.

Silhouette Coefficient Method: Μια υψηλότερη βαθμολογία σχετίζεται με ένα μοντέλο με καλύτερα καθορισμένα cluster. Ο Silhouette Coefficient ορίζεται για κάθε δείγμα και αποτελείται από δύο βαθμολογίες:

  • Η μέση απόσταση μεταξύ ενός δείγματος και όλων των άλλων σημείων της ίδιας κατηγορίας.
  • Η μέση απόσταση μεταξύ ενός δείγματος και όλων των άλλων σημείων στο επόμενο κοντινότερο σύμπλεγμα.

Ένας υψηλότερος συντελεστής υποδηλώνει ότι το αντικείμενο ταιριάζει καλά με το δικό του cluster και πως δεν ταιριάζει με γειτονικά cluster.

The Elbow Method - inertia: Το Elbow Method είναι μια τεχνική για τον προσδιορισμό του βέλτιστου αριθμού cluster με βάση το σχήμα του διαγράμματος inertia. Στο πλαίσιο της ομαδοποίησης, η inertia (αδράνεια) είναι μια μέτρηση που χρησιμοποιείται για την αξιολόγηση της ποιότητας των αποτελεσμάτων της ομαδοποίησης. Μετρά το άθροισμα των τετραγώνων αποστάσεων μεταξύ κάθε σημείου δεδομένων και του κέντρου του cluster του. Ο στόχος των αλγορίθμων ομαδοποίησης όπως το K-means είναι η ελαχιστοποίηση της inertia.

inertia = []
silhouetteScore = []
# υπολογισμός silhouette score και inertia
for i in range(2, 12):
    kmeans = KMeans(n_clusters=i, init='k-means++', random_state=0)
    kmeans.fit(scaled_df)
    inertia.append(kmeans.inertia_)
    silhouetteScore.append(silhouette_score(scaled_df, kmeans.predict(scaled_df)))

# Plot
plt.figure(figsize=(10,5))

plt.subplot(1, 2, 1)
plt.plot(range(2, 12), inertia)
plt.title('The Elbow Method')
plt.xlabel('Number of clusters')
plt.ylabel('Inertia')

plt.subplot(1, 2, 2)
plt.plot(range(2, 12), silhouetteScore)
plt.title('Silhouette Score')
plt.xlabel('Number of clusters')
plt.ylabel('Silhouette Score')

plt.tight_layout()
plt.show()

Θέλουμε να διαλέξουμε ένα σημείο όπου το inertia είναι χαμηλό και το silhouette score ψηλό. Σύμφωνα με τα παραπάνω γραφήματα, παρατηρούμε να ισχύουν ταυτόχρονα οι δύο προυποθέσεις στα 9 cluster.

kmeans = KMeans(n_clusters=9)
kmeans.fit(scaled_df)
# Αντιστοίχιση label σε cluster
cluster_labels = KMeans(n_clusters=9).fit_predict(scaled_df)

Μείωση διαστάσεων με PCA

Οι αλγόριθμοι μείωσης διαστάσεων μειώνουν τον αριθμό των χαρακτηριστικών στα δεδομένα, ενώ διατηρούν όσο το δυνατόν περισσότερες πληροφορίες. Αυτό μπορεί να είναι χρήσιμο για την οπτικοποίηση δεδομένων υψηλών διαστάσεων ή για τη μείωση της πολυπλοκότητας των δεδομένων για περαιτέρω ανάλυση.

Το παρακάτω απόσπασμα κώδικα εκτελεί PCA στο scaled_df DataFrame και δημιουργεί ένα νέο DataFrame X_PCA με τα κύρια χαρακτηριστικά που εξάγονται από τα δεδομένα.

dist = 1-cosine_similarity(scaled_df)

# Εφαρμόζουμε PCA για να οπτικοποιήσουμε στις 2 διαστάσεις
pca = PCA(2)
pca.fit(dist)
X_PCA = pca.transform(dist)

# Οπτικοποίηση των clusters
plt.figure(figsize=(10,8))
sns.scatterplot(x=X_PCA[:, 0], y=X_PCA[:, 1], 
                hue=cluster_labels, palette=sns.color_palette('hls', kmeans.cluster_centers_.shape[0]), s=50)
plt.title('Cluster of Customers', size=15, pad=10)
plt.legend(loc=0, bbox_to_anchor=[1,1])
plt.show()

Η συνάρτηση cosine_similarity υπολογίζει την ομοιότητα συνημιτόνου ανά ζεύγη μεταξύ των σειρών του πίνακα scaled_df. Εφόσον το PCA λειτουργεί με αποστάσεις, η αφαίρεση της ομοιότητας συνημιτόνου από το 1 το μετατρέπει σε μέτρο απόστασης. Αυτό το βήμα είναι απαραίτητο επειδή το PCA στοχεύει να βρει τις κατευθύνσεις της μέγιστης διακύμανσης στα δεδομένα και τα μέτρα απόστασης είναι καλύτερα κατάλληλα για αυτόν τον σκοπό.

Ανάλυση των clusters

Για λόγους ανάλυσης, θα επιλέξουμε μόνο τα παρακάτω αριθμητικά χαρακτηριστικά για οπτικοποίηση των cluster.

numerical_features2 = ['BALANCE', 'PURCHASES', 'CREDIT_LIMIT', 'PAYMENTS', 'TENURE']
# Οπτικοποίηση των cluster σε κάθε μεταβλητή
df['cluster'] = cluster_labels
for feature in numerical_features2:
    plot_num_cat(feature, 'cluster')

df_out = df.groupby(by='cluster').sum()[['BALANCE', 'PURCHASES', 'CREDIT_LIMIT', 'PAYMENTS', 'TENURE']].reset_index()
df_out.head(3)

sns.set_style("darkgrid")
plt.figure(figsize=(22, 4))
plt.subplot(1, 5, 1)
sns.barplot(x='cluster', y='BALANCE', data=df_out, palette='crest', seed=123)
plt.subplot(1, 5, 2)
sns.barplot(x='cluster', y='PURCHASES', data=df_out, palette='crest', seed=123)
plt.subplot(1, 5, 3)
sns.barplot(x='cluster', y='CREDIT_LIMIT', data=df_out, palette='crest', seed=123)
plt.subplot(1, 5, 4)
sns.barplot(x='cluster', y='PAYMENTS', data=df_out, palette='crest', seed=123)
plt.subplot(1, 5, 5)
sns.barplot(x='cluster', y='TENURE', data=df_out, palette='crest', seed=123)
<Axes: xlabel='cluster', ylabel='TENURE'>

Από τα παραπάνω γραφήματα, μπορούμε να λάβουμε πληροφορίες σχετικά με τις πιστωτικές κάρτες που ανήκουν σε άτομα κάθε cluster. Για παράδειγμα, τα άτομα από το cluster 1 έχουν υψηλό ποσό υπολοίπου στον λογαριασμό τους, όπως και όριο κάρτας και περίοδο ισχύος της κάρτας ενώ έχουν μεσαίο ποσό αγορών από χρήστη και χαμηλό ποσο αγορών από λογαριασμό.

Ιεραχική ομαδοποίηση

Η ιεραρχική ομαδοποίηση είναι μια τεχνική ομαδοποίησης που oμαδοποιεί παρόμοια σημεία δεδομένων σε clusters με βάση την κοντινότητά τους. Σε αντίθεση με άλλους αλγόριθμους ομαδοποίησης που απαιτούν τον καθορισμό του αριθμού των cluster εκ των προτέρων, η ιεραρχική ομαδοποίηση δημιουργεί μια ιεραρχία cluster που μπορεί να αναπαρασταθεί ως δενδρόγραμμα.

Agglomerative: Γνωστή και ως bottom-up clustering ξεκινά με μεμονωμένα σημεία δεδομένων ως ξεχωριστά clusters και σταδιακά τα συγχωνεύει με βάση ένα μέτρο ομοιότητας έως ότου ληφθεί ένας καθορισμένος αριθμός cluster, στη περίπτωσή μας 9.

# Δενδρόγραμμα
dendro = dendrogram(linkage(scaled_df, method='ward'))
plt.plot([250]*20000, color='r')
plt.plot([150]*20000, color='r')
plt.show()
# Bottom-up Ιεραρχική ομαδοποίηση
model = AgglomerativeClustering(n_clusters=9, affinity='euclidean', linkage='ward')

# Αντιστοίχιση label σε cluster
hierarchical = model.fit_predict(scaled_df)
hierarchical_labels = hierarchical

Ακολουθεί η οπτικοποίηση της ιεραρχικής ομαδοποίησης.

dist = 1-cosine_similarity(scaled_df)
# Μείωση διαστάσεων στις 2D
pca = PCA(2)
pca.fit(dist)
X_PCA = pca.transform(dist)

# Οπτικοποίηση των clusters
plt.figure(figsize=(10,8))
sns.scatterplot(x=X_PCA[:, 0], y=X_PCA[:, 1], 
                hue=hierarchical, palette=sns.color_palette('hls', len(np.unique(hierarchical))), s=50)
plt.title('Cluster of Customers', size=15, pad=10)
plt.legend(loc=0, bbox_to_anchor=[1,1])
plt.show()

Ανάλυση clusters

# Οπτικοποίηση των cluster σε κάθε μεταβλητή
df['cluster'] = hierarchical_labels
for feature in numerical_features2:
    plot_num_cat(feature, 'cluster')

df_out = df.groupby(by='cluster').sum()[['BALANCE', 'PURCHASES', 'CREDIT_LIMIT', 'PAYMENTS', 'TENURE']].reset_index()
df_out.head(3)

sns.set_style("darkgrid")
plt.figure(figsize=(22, 4))
plt.subplot(1, 5, 1)
sns.barplot(x='cluster', y='BALANCE', data=df_out, palette='crest', seed=123)
plt.subplot(1, 5, 2)
sns.barplot(x='cluster', y='PURCHASES', data=df_out, palette='crest', seed=123)
plt.subplot(1, 5, 3)
sns.barplot(x='cluster', y='CREDIT_LIMIT', data=df_out, palette='crest', seed=123)
plt.subplot(1, 5, 4)
sns.barplot(x='cluster', y='PAYMENTS', data=df_out, palette='crest', seed=123)
plt.subplot(1, 5, 5)
sns.barplot(x='cluster', y='TENURE', data=df_out, palette='crest', seed=123)
<Axes: xlabel='cluster', ylabel='TENURE'>

Παρόμοιες πληροφορίες με εκείνες που αναφέρθηκαν στο k-Means μπορούν να παρατηρηθούν και να εξαχθούν από τα παραπάνω γράφηματα.